package com.sensorfriendly.ui;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Random;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.speech.RecognizerIntent;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
import android.view.Display;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
import android.view.animation.RotateAnimation;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.sensorfriendly.AppConstants;
import com.sensorfriendly.AppContext;
import com.sensorfriendly.R;
import com.sensorfriendly.model.Scenario;
import com.sensorfriendly.util.CallUtil;
import com.sensorfriendly.util.ImageUtil;

public class DashboardNavigationActivity extends Activity implements
		OnClickListener, OnInitListener, OnUtteranceCompletedListener {

	private static final int VTT_CHECK = 0;
	private static final int TTS_CHECK = VTT_CHECK + 1;

	private GestureDetector gesturedetector = null;
	private View layout;
	private List<Scenario> currentOptions;
	private TextToSpeech myTTS = null;
	private SharedPreferences sharePrefs;
	private Random r;
	private Animation animShake;
	private Animation animZoom;
	private int currentSpeechIndex;
	private TypedArray colorTheme;
	private int pageNum;
	@Override
	public void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
		setContentView(R.layout.dashboard_layout);
		
		AppContext app = (AppContext) getApplication();
		Bundle extra = this.getIntent().getExtras();
		
		pageNum = app.getCurrentPage();
		
	}
	
	
	/**
	 * initialize the necessary variables for this UI class.
	 */
	private void activityPreparation() {
		AppContext app = (AppContext) getApplication();
		pageNum = app.getCurrentPage();
		//get the scenario items needs to be displayed on this page.
		currentOptions = app.getCurrentPageScenearios(pageNum);
		
		//initialize getsture handler for swiping
		handleGestureListner();
		
		// checking if user wants to use speech recgonition for recognition.
		// if user does want to use it. the button to trigger it will be hidden.
		Button speakButton = (Button) findViewById(R.id.footer_button);
		sharePrefs = PreferenceManager.getDefaultSharedPreferences(this);
		boolean speechActivated = sharePrefs.getBoolean(
				AppConstants.PREFERENCE_SPEECH_ACTIVATED, true);
		if (speechActivated) {
			PackageManager pm = getPackageManager();
			List<ResolveInfo> activities = pm.queryIntentActivities(new Intent(
					RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
			if (activities.size() == 0 || !isOnline()) {
				speakButton.setEnabled(false);
				speakButton.setVisibility(View.GONE);
				Editor editor = sharePrefs.edit();
				editor.putBoolean(AppConstants.PREFERENCE_SPEECH_ACTIVATED, false);
				editor.commit();
			} else {
				speakButton.setOnClickListener(this);
			}
		} else {
			speakButton.setEnabled(false);
			speakButton.setVisibility(View.GONE);
		}
		
		//randomly select a color scheme for the page
		r = new Random();
		int colorThemeIndex = Math.abs(r.nextInt()) % app.getThemeIds().length;
		colorTheme = getResources().obtainTypedArray(app.getThemeIds()[colorThemeIndex]);
		int color = colorTheme.getColor(0, Color.WHITE);
		((LinearLayout) findViewById(R.id.home_root)).setBackgroundColor(color);
		
		//loading pre-defined animations
		animShake= AnimationUtils.loadAnimation(this, R.anim.shake);
		animZoom = AnimationUtils.loadAnimation(this, R.anim.zoomin);
		
		for(int j = 0; j<4; j++){
			((View) findViewById(app.getButtonBGIds()[j])).setVisibility(View.VISIBLE);
		}
		initializeButtons();
	}
	
	
	/**
	 * initialize a button for each one of the scenarios and start their animation. 
	 */
	private void initializeButtons() {
		AppContext app = (AppContext) getApplication();
		int i;
		for (i = 0; i < currentOptions.size(); i++) {
			View v = (View) findViewById(app.getButtonBGIds()[i]);
			v.setBackgroundColor(colorTheme.getColor(i+1, Color.WHITE));
			Button button = (Button) findViewById(app.getButtonIds()[i]);
			button.setText(currentOptions.get(i).getName());
			
			// loading the custom icon for the scenario.
			Drawable icon =null;
			InputStream is = null;
			try {
				if(currentOptions.get(i).getIconPath() != null){
					is = getAssets().open(currentOptions.get(i).getIconPath());
				}
			} catch (IOException e) {
				// TODO SENSOR handle image loading failed
				e.printStackTrace();
			}
			if(is !=null){
			// First decode with inJustDecodeBounds=true to check dimensions
				final BitmapFactory.Options options = new BitmapFactory.Options();
				options.inJustDecodeBounds = true;
				BitmapFactory.decodeStream(is, null, options);
				Display display = getWindowManager().getDefaultDisplay(); 
				int screenWidth = display.getWidth();
				int screenHeight = display.getHeight();
				int reqWidth = screenWidth/5;
				int reqHeight = screenWidth/5;
				options.inSampleSize = ImageUtil.calculateInSampleSize(options, reqWidth, reqHeight);
				// Decode bitmap with inSampleSize set
				options.inJustDecodeBounds = false;
				Bitmap image = BitmapFactory.decodeStream(is,null, options);
				icon = new BitmapDrawable(image);
			}
			//if nothing is loaded use the default icon.
			if(icon == null){
				button.setCompoundDrawablesWithIntrinsicBounds(null, getResources()
						.getDrawable(R.drawable.windows), null, null);

			}else{
				button.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null);

			}
			
			// check if the user wants animation.
			if (sharePrefs.getBoolean(
					AppConstants.PREFERENCE_ANIMATION_ACTIVATED, true)) {
				final RotateAnimation animRotate = new RotateAnimation(-45.0f,
						45.0f, RotateAnimation.RELATIVE_TO_SELF, 0.5f,
						RotateAnimation.RELATIVE_TO_SELF, 0.5f);

				

				animRotate.setRepeatCount(Animation.INFINITE);
				animRotate.setRepeatMode(Animation.REVERSE);
				animRotate.setDuration((long) (r.nextFloat() * 1000) + 500);
				button.startAnimation(animRotate);

			}
			button.setOnClickListener(this);
		}
		// if there are less than 4 scenarios to display, then hide the buttons that are not needed.
		if(i<4){
			for(int j = i; j<4; j++){
				((View) findViewById(app.getButtonBGIds()[j])).setVisibility(View.INVISIBLE);
			}
		}
		
		((ImageView) findViewById(R.id.ab_settings)).setOnClickListener(this);
		((ImageView) findViewById(R.id.ab_home)).setOnClickListener(this);
		((ImageView) findViewById(R.id.ab_call)).setOnClickListener(this);
		
		((TextView) findViewById(R.id.page_title)).setText("Scenarios");
	}

	@Override
	protected void onResume() {
		super.onResume();
		activityPreparation();
		// check if the TTS service should be started.
		boolean audioActivated = sharePrefs.getBoolean(
				AppConstants.PREFERENCE_AUDIO_ACTIVATED, true);
		if (audioActivated) {
			if (myTTS == null) {
				// check for TTS data
				Intent checkTTSIntent = new Intent();
				checkTTSIntent
						.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
				startActivityForResult(checkTTSIntent, TTS_CHECK);
			}
		}
	}

	@Override
	protected void onPause() {
		super.onPause();
		// stop TTs and release if the user navigates away.
		if (myTTS != null) {
			myTTS.stop();
			myTTS.shutdown();
			myTTS = null;
		}
		if (sharePrefs.getBoolean(
				AppConstants.PREFERENCE_ANIMATION_ACTIVATED, true)) {
			AppContext app = (AppContext) getApplication();
			for (int i = 0; i < currentOptions.size(); i++) {
				View v = (View) findViewById(app.getButtonBGIds()[i]);
				v.setBackgroundColor(colorTheme.getColor(i+1, Color.WHITE));
				Button button = (Button) findViewById(app.getButtonIds()[i]);
				button.clearAnimation();
			}
		}
		System.gc();
		
	}

	public boolean dispatchTouchEvent(MotionEvent ev) {
		super.dispatchTouchEvent(ev);
		return gesturedetector.onTouchEvent(ev);
	}

	/*-------------------------------------*
	 * This function is used to setup the  *
	 * Listener to detect "touch" events   *
	 *-------------------------------------*/
	private void handleGestureListner() {
		gesturedetector = new GestureDetector(new MyGestureListener());
		layout = (LinearLayout) findViewById(R.id.home_root);
		layout.setOnTouchListener(new OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
				gesturedetector.onTouchEvent(event);
				return true;
			}
		});
	}

	private void navigateHandler(String category) {
		// Toast.makeText(this, "Text: "+category, Toast.LENGTH_LONG).show();
		Intent messageIntent = new Intent(this, MessageActivity.class);
		messageIntent.putExtra(AppConstants.CATEGORY, category);
		startActivity(messageIntent);
	}
	
	/**
	 * click listenner that handles click event of multiple UI components on the page.
	 */
	@Override
	public void onClick(View v) {
		//Footer button triggers the speech regonition activity.
		if (v.getId() == R.id.footer_button) {
			startVoiceRecognitionActivity();
		} 
		//below handles buttons on the action bar.
		else if (v.getId() == R.id.ab_settings) {
			Intent settingsIntent = new Intent(this, SettingsActivity.class);
			startActivity(settingsIntent);
		} else if (v.getId() == R.id.ab_call) {
			String phoneNum = PreferenceManager.getDefaultSharedPreferences(this).getString(AppConstants.PREFERENCE_EMERGENCY_NUMBER, "");
			if(phoneNum.length()>1){
				CallUtil.call(this, phoneNum);
			}
		} else if (v.getId() == R.id.ab_home) {
			Intent homeIntent = new Intent(this, DashboardNavigationActivity.class);
			homeIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
					| Intent.FLAG_ACTIVITY_SINGLE_TOP);
			startActivity(homeIntent);
		} 
		// navigate to the messages page based on the scenario selection.
		else {
			navigateHandler(((Button) v).getText().toString());
		}
	}
	
	/**
	 *  checking if device is connected to the internet.
	 * @return
	 */
	private boolean isOnline() {
		ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo netInfo = cm.getActiveNetworkInfo();
		if (netInfo != null && netInfo.isConnected()) {
			return true;
		}
		return false;
	}
	
	// put the paramters to start voice recognition activity.
	private void startVoiceRecognitionActivity() {
		Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
		intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
				RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
		intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 5);
		intent.putExtra(RecognizerIntent.EXTRA_PROMPT,
				"Speak the option you want to select...");
		startActivityForResult(intent, VTT_CHECK);
	}

	/*-------------------------------------*
	 * Handle the results from the voice   *
	 * recognition activity.              
	 * And check if TTS service is available *
	 *-------------------------------------*/
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (requestCode == VTT_CHECK && resultCode == RESULT_OK) {
			ArrayList<String> matches = data
					.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
			checkMatches(matches);
		} else if (requestCode == TTS_CHECK) {
			if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS ) {
				myTTS = new TextToSpeech(this, this);
			} else {
//				Toast.makeText(this, "Speech data not available",
//						Toast.LENGTH_LONG).show();
				Editor editor = sharePrefs.edit();
				editor.putBoolean(AppConstants.PREFERENCE_AUDIO_ACTIVATED,
						false);
				editor.commit();
			}
		}
		super.onActivityResult(requestCode, resultCode, data);
	}
	
	
	//find string matches.
	private void checkMatches(List<String> matches) {
		for (Scenario option : currentOptions) {
			for (String match : matches) {
				//System.out.println(match);
				if (match.toLowerCase().contains(option.getName().toLowerCase())) {
					navigateHandler(option.getName());
					return;
				}
			}
		}
	}

	// setup TTS
	public void onInit(int initStatus) {
		// check for successful instantiation
		if (initStatus == TextToSpeech.SUCCESS && myTTS !=null) {
			int availability = myTTS.isLanguageAvailable(Locale.US);
			if (availability == TextToSpeech.LANG_AVAILABLE
					|| availability == TextToSpeech.LANG_COUNTRY_AVAILABLE
					|| availability == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) {
				myTTS.setLanguage(Locale.US);
				myTTS.setOnUtteranceCompletedListener(this);
				speakOptions();
			}
		} else if (initStatus == TextToSpeech.ERROR) {
			Toast.makeText(this, "Sorry! Text To Speech failed...",
					Toast.LENGTH_LONG).show();
		}
	}
	
	
	// Speak the options/scenarios that user can choose.
	private void speakOptions() {
		StringBuilder sb = new StringBuilder();
		boolean speechActivated = sharePrefs.getBoolean(
				AppConstants.PREFERENCE_SPEECH_ACTIVATED, true);
		if(speechActivated){
			sb.append("How can I help you? You can say, ");
		}else{
			sb.append("You can select, ");
		}
		
		HashMap<String, String> speechParmas = new HashMap<String, String>();
		speechParmas.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "0");		
		myTTS.speak(sb.toString(), TextToSpeech.QUEUE_FLUSH, speechParmas);
	}
	
	// flipping to the preivous pagee.
	private void previous() {
		AppContext app = (AppContext) getApplication();
		int prevPageNum = app.previousPage();

		Intent previousPage = new Intent(getApplicationContext(),
				DashboardNavigationActivity.class);
		previousPage.putExtra(AppConstants.PAGE_NUM_KEY, prevPageNum);
		startActivity(previousPage);

		overridePendingTransition(R.anim.slide_right_incoming,
				R.anim.slide_right_outgoing);
		finish();
	}

	//flipping to the next pagee.
	private void next() {
		AppContext app = (AppContext) getApplication();
		int nextPageNum = app.nextPage();

		Intent nextPage = new Intent(getApplicationContext(),
				DashboardNavigationActivity.class);
		nextPage.putExtra(AppConstants.PAGE_NUM_KEY, nextPageNum);
		startActivity(nextPage);

		overridePendingTransition(R.anim.slide_left_incoming,
				R.anim.slide_left_outgoing);
		finish();
	}

	/*---------------------------------------------*
	 * The Gesture class that detects the gesture  *
	 * and determines if it is appropriate enough  *
	 * to be considered a left/right swipe         *
	 *---------------------------------------------*/
	class MyGestureListener extends GestureDetector.SimpleOnGestureListener {

		private static final int SWIPE_MIN_DISTANCE = 30;
		private static final int SWIPE_MAX_OFF_PATH = 250;
		private static final int SWIPE_THRESHOLD_VELOCITY = 30;

		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
				float velocityY) {

			float dX = e2.getX() - e1.getX();
			float dY = e1.getY() - e2.getY();

			if (Math.abs(dY) < SWIPE_MAX_OFF_PATH
					&& Math.abs(velocityX) >= SWIPE_THRESHOLD_VELOCITY
					&& Math.abs(dX) >= SWIPE_MIN_DISTANCE) {

				if (dX > 0) {
					previous();
				} else {
					next();
				}
				return true;
			}
			return false;
		}
	} // End MyGestureListner class

	private void startButtonAnim(int optionIndex){
		AppContext app = (AppContext) getApplication();
		Button button = (Button) findViewById(app.getButtonIds()[currentSpeechIndex-1]);
		final RotateAnimation animRotate = new RotateAnimation(-45.0f,
				45.0f, RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		animRotate.setRepeatCount(Animation.INFINITE);
		animRotate.setRepeatMode(Animation.REVERSE);
		animRotate.setDuration((long) (r.nextFloat() * 1000) + 500);
		button.startAnimation(animRotate);
	}
	
	
	// Synchronize the animation with the word that is being spoken.
	@Override
	public void onUtteranceCompleted(String utteranceId) {
		int speechOptionIndex = Integer.parseInt(utteranceId);
		currentSpeechIndex = speechOptionIndex;
		if(currentSpeechIndex>0){
			runOnUiThread(new Runnable() {
				@Override
				public void run() {
					startButtonAnim(currentSpeechIndex);
				}
			} );
		}
		if(speechOptionIndex<currentOptions.size()){
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			String option = currentOptions.get(speechOptionIndex).getName();
			speechOptionIndex++;
			HashMap<String, String> speechParmas = new HashMap<String, String>();
			speechParmas.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, Integer.toString(speechOptionIndex));	
			myTTS.speak(option+". ", TextToSpeech.QUEUE_FLUSH, speechParmas);
			
			runOnUiThread(new Runnable() {
				@Override
				public void run() {
					AppContext app = (AppContext) getApplication();
					Button button = (Button) findViewById(app.getButtonIds()[currentSpeechIndex]);
					AnimationSet animSet = new AnimationSet(false);
					animSet.addAnimation(animZoom);
					animSet.addAnimation(animShake);
					button.startAnimation(animSet);
				}
			} );
			
		}
	}

}
